نفوذ به قلب SQL Server با sys.sp_dbmmonitorupdate

نفوذ به قلب SQL Server با sys.sp_dbmmonitorupdate


قانون مهم در امنیت پایگاه داده این است: "هرگز به ورودی کاربر اعتماد نکن!" اما اگر آسیب پذیری در کدی باشد که توسط خود مایکروسافت نوشته و امضا شده و تمام سیستم های امنیتی به آن اعتمادِ مطلق دارند، چه؟

اخیرا یک آسیب پذیری SQL Injection بسیار خاص (CVE-2025-53727) فاش شد که در یکی از Stored Procedureهای سیستمی و معتبر SQL Server به نام sys.sp_dbmmonitorupdate قرار داشت.

چیزی که این باگ را به یک تهدید جدی تبدیل کرد، دو چیز بود:

  • این رویه یک System Object است و موتور دیتابیس به آن دسترسی بالایی می دهد.
  • کد این تابع ظاهرا ایمن نوشته شده و ورودی ها را Escape می کند؛ اما یک تبدیل نوع ساده و بی سروصدا، کل امنیت آن را فرو ریخت.

رویه sys.sp_dbmmonitorupdate چیست و چرا خطرناک بود؟

این Stored Procedure سیستمی وظیفه پایش و به روزرسانی وضعیت قابلیت Database Mirroring را بر عهده دارد و پارامتری به نام @database_name را می پذیرد.

چون این تابع مستقیماً متعلق به خودِ موتور SQL Server است:

  • توسط سیستم به عنوان یک موجودیت امن و قابل اعتماد شناسایی می شود.
  • تحت شرایط خاصی ممکن است با دسترسی های بالاتری اجرا شود.
  • ابزارهای اسکن امنیتی معمولاً کدهای داخلی سیستمی مایکروسافت را اسکن یا مسدود نمی کنند.

فیلتری که دور زده شد:
درون این تابع سیستمی، کدی برای اجرای یک دستور داینامیک وجود داشت که به شکل زیر نوشته شده بود:

DECLARE @command CHAR(256); -- به نوع داده CHAR (غیر یونیکد) دقت کنید!

-- تلاش برای ایمن سازی با دوبرابر کردن کوتیشن ها
SET @command = N'sys.sp_dbmmonitorresults '''
+ REPLACE(@database_name, N'''', N'''''')
+ N''',0,0';

EXEC (@command);

در نگاه اول، مایکروسافت کارش را درست انجام داده است. آنها با دستور REPLACE تمام کوتیشن های معمولی (') را دو برابر کرده اند تا جلوی شکسته شدن رشته را بگیرند. اما یک تله ی بزرگ در این میان وجود دارد: @database_name یک متغیر NVARCHAR است، اما متغیر @command از نوع CHAR تعریف شده است.

در دنیای یونیکد کاراکتری داریم به نام modifier letter apostrophe (ʼ) با کد U+02BC که از نظر ظاهری کپی برابر اصل کوتیشن معمولی است اما کد متفاوتی دارد.

حمله در ۳ مرحله رخ می داد:
1- نفوذگر نام دیتابیس مخرب خود را با کاراکتر U+02BC ارسال می کرد. از آنجا که تابع REPLACE مایکروسافت فقط به دنبال کوتیشن استاندارد (U+0027) می گشت، این کاراکتر را فیلتر نمی کرد و رشته بدون تغییر عبور می کرد.
2- در خط بعدی، رشته یونیکد حاصل قرار است درون متغیر @command که از نوع غیر یونیکد (CHAR) است ریخته شود. در این لحظه SQL Server فرآیند Implicit Conversion را شروع می کند.
3- از آنجا که کاراکتر یونیکد U+02BC معادل مستقیمی در کدهای تک بایتی ASCII ندارد، سیستم طبق مکانیزم Best-fit mapping آن را به شبیه ترین کاراکتر ممکن یعنی کوتیشن واقعی (U+0027) تبدیل می کند!

نتیجه نهایی: کوتیشن مخرب بعد از رد شدن از سدِ فیلتر امنیتی مایکروسافت ایجاد می شد، رشته داینامیک را می شکست و دستور تزریق شده مهاجم را اجرا می کرد.

درس های مهم برای متخصصان پایگاه داده و برنامه نویسان:
این آسیب پذیری پُر سر و صدا که اکنون توسط مایکروسافت وصله شده است، سه درس کلیدی برای ما دارد:

  • اعتماد کاذب به اشیای سیستمی نداشته باشید: هیچ کدی، حتی کدهایی که توسط توسعه دهندگان موتورهای دیتابیس بزرگ نوشته شده اند، از خطای انسانی مصون نیستند.
  • نوع داده ها (Data Types) نقش امنیتی دارند: تفاوت VARCHAR و NVARCHAR صرفاً بحث فضا و زبان نیست. تبدیل های ضمنی غیر یونیکد به یونیکد و برعکس می توانند رفتار داده ها را به کل تغییر داده و فیلترهای امنیتی شما را خنثی کنند.
  • از پارامترها استفاده کنید، نه REPLACE: راهکار نهایی حذف کامل الحاق رشته در متغیرهای داینامیک و استفاده از پارامتردهی با sp_executesql است.
سید حامد واحدی سید حامد واحدی     1 مرداد 1405